# !/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import shutil
import sys
from os import listdir
from PySide6.QtCore import QSettings, QPoint, SIGNAL, QSize, Qt
from PySide6.QtGui import QStandardItemModel, QIcon, QStandardItem, QPainter, QColor, QTextCursor, QBrush
from PySide6.QtWidgets import QMainWindow, QApplication, QListView, QPushButton, QListWidget, QLabel, QHeaderView, \
    QFileDialog, QMessageBox, QRadioButton, QCheckBox, QTextEdit, QTabWidget
from PySide6 import QtWidgets, QtCore
from UI.ui.homepage import Ui_mainWindow
from UI.ui_lib.parse_xml import get_slot_and_hyper_period, init_topo, get_link_node_port_set, \
    get_stream, get_plan, get_inject_tbl, get_map_tbl, get_tbl, get_gcl_tbl, get_para_tbl, get_aux_tbl, get_cb_tbl
from UI.ui_lib.ui_structure import TSNButton, topoPaintWidget, streamListWidget, topoListWidget
from util.processing import processing
from bin.assembler import assemble
from bin.linker import link
from OpenPlanner.core.open_planner import open_planner
import qdarkstyle
from util.json_utils import xml_file_to_json_file
import json
from pygments import highlight, lexers, formatters


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.ui = Ui_mainWindow()
        self.ui.setupUi(self)

        # 主窗口自动填满屏幕
        # self.resize(QApplication.instance().screens()[0].size(), )

        self.setWindowIcon(QIcon('img/fenglin_logo.png'))

        # 欢迎按钮的显示
        self.logo_button_show()

        # UI组件相关
        self.topoListWidget = None
        self.planWidget = None
        self.tsn_button_set = []
        self.flowFeaturePage_label_text = ''
        self.flowLabelWidget = None

        # 管理流程的flag
        self.flowFeatureFlag = False
        self.planFlag = False
        self.assembleFlag = False
        self.linkFlag = False
        self.saveFlag = False

        self.ui.flowFeatureButton.setEnabled(False)
        self.ui.planButton.setEnabled(False)
        self.ui.assembleButton.setEnabled(False)
        self.ui.linkerButton.setEnabled(False)
        self.ui.saveButton.setEnabled(False)

        # 关键数据
        self.inject_tbl = []
        self.map_tbl = []
        self.fwd_tbl = []
        # self.inform_tbl = []
        # 下面这两个表的初始化方式不同于上述几个表
        self.gcl_tbl = []
        self.para_tbl = []
        self.cb_tbl = []
        # self.aux_tbl = []
        self.streamListWidget = None
        self.currentSWItem = None

        # 创建QSettings，配置文件是tmp/.temp
        self.setting = QSettings('tmp/.temp', QSettings.IniFormat)
        # 读取上一次的目录路径
        self.last_path = self.setting.value('LastFilePath')
        # 如果字符串为空，将路径索引到根目录
        if self.last_path is None:
            self.last_path = '../input'  # 根盘符

        '''算法选择的按钮绑定'''
        self.ui.selectButton.clicked.connect(self.select_func)
        self.ui.frameButton.clicked.connect(self.frame_text_show)
        self.ui.windowButton.clicked.connect(self.window_text_show)
        self.ui.xzButton.clicked.connect(self.xz_text_show)
        self.ui.itpButton.clicked.connect(self.itp_text_show)
        self.ui.frameCheckButton.clicked.connect(self.frame_file_show)
        self.ui.frameModifyButton.clicked.connect(self.frame_file_modify)
        self.ui.frameSaveButton.clicked.connect(self.frame_file_save)
        self.ui.flowCheckButton.clicked.connect(self.flow_file_show)
        self.ui.flowModifyButton.clicked.connect(self.flow_file_modify)
        self.ui.flowSaveButton.clicked.connect(self.flow_file_save)
        self.ui.topoCheckButton.clicked.connect(self.topo_file_show)
        self.ui.topoModifyButton.clicked.connect(self.topo_file_modify)
        self.ui.topoSaveButton.clicked.connect(self.topo_file_save)
        self.ui.completeButton.clicked.connect(self.plan)

        '''流特征结果显示的按钮绑定'''
        self.ui.flowFeatureButton.clicked.connect(self.flow_feature_func)

        '''规划结果显示的按钮绑定'''
        self.ui.planButton.clicked.connect(self.plan_func)

        '''编译结果显示'''
        self.ui.assembleButton.clicked.connect(self.assemble_func)
        self.ui.tabWidget.currentChanged.connect(self.tab_click_func)
        self.ui.tabWidget.setStyleSheet(
            'QTableCornerButton::section{'
            'border-top:0px solid rgb(0, 0, 0);'
            'border-left:0px solid rgb(0, 0, 0);'
            'border-right:0.5px solid rgb(0, 0, 0);'
            'border-bottom: 0.5px solid rgb(0, 0, 0);'
            'background-color:white;'
            'color: rgb(0, 0, 0);'
            '}'
            "QHeaderView::section{"
            "border-top: 0px solid rgb(0, 0, 0);"
            "border-left: 0px solid rgb(0, 0, 0);"
            "border-right: 0.5px solid rgb(0, 0, 0);"
            "border-bottom: 0.5px solid rgb(0, 0, 0);"
            "background-color:white;"
            "padding:4px;"
            "color: rgb(0, 0, 0);"
            "}"
        )
        # self.ui.forwardTbl.setStyleSheet("QWidget{font:13pt;"
        #                                  "background-color: rgb(255, 255, 255);"
        #                                  "color: rgb(0, 0, 0);}")
        # self.ui.paraTbl.setStyleSheet("QWidget{font:13pt;"
        #                               "background-color: rgb(255, 255, 255);"
        #                               "color: rgb(0, 0, 0);}")
        # self.ui.gclTbl.setStyleSheet("QWidget{font:13pt;"
        #                              "background-color: rgb(255, 255, 255);"
        #                              "color: rgb(0, 0, 0);}")
        # self.ui.mapTbl.setStyleSheet("QWidget{font:13pt;"
        #                              "background-color: rgb(255, 255, 255);"
        #                              "color: rgb(0, 0, 0);}")
        # self.ui.injectTbl.setStyleSheet("QWidget{font:13pt;"
        #                                 "background-color: rgb(255, 255, 255);"
        #                                 "color: rgb(0, 0, 0);}")
        # self.ui.cbTbl.setStyleSheet("QWidget{font:13pt;"
        #                             "background-color: rgb(255, 255, 255);"
        #                             "color: rgb(0, 0, 0);}")

        '''链接结果显示的按钮绑定'''
        self.ui.linkerButton.clicked.connect(self.link_func)

        icon_1 = QIcon()
        icon_1.addFile('./img/import_file.png')
        self.ui.selectButton.setIcon(icon_1)
        self.ui.selectButton.setIconSize(QSize(26, 26))

        icon_2 = QIcon()
        icon_2.addFile('./img/circle.png')
        self.ui.flowFeatureButton.setIcon(icon_2)
        self.ui.flowFeatureButton.setIconSize(QSize(26, 26))

        icon_3 = QIcon()
        icon_3.addFile('./img/plan.png')
        self.ui.planButton.setIcon(icon_3)
        self.ui.planButton.setIconSize(QSize(26, 26))

        icon_4 = QIcon()
        icon_4.addFile('./img/assem.png')
        self.ui.assembleButton.setIcon(icon_4)
        self.ui.assembleButton.setIconSize(QSize(26, 26))

        icon_5 = QIcon()
        icon_5.addFile('./img/config.png')
        self.ui.linkerButton.setIcon(icon_5)
        self.ui.linkerButton.setIconSize(QSize(24, 24))

        icon_6 = QIcon()
        icon_6.addFile('./img/save.png')
        self.ui.saveButton.setIcon(icon_6)
        self.ui.saveButton.setIconSize(QSize(24, 24))

        '''保存按钮'''
        self.ui.saveButton.clicked.connect(self.save_file)

        # 显示第一页
        self.ui.contentStackedWidget.setCurrentIndex(0)

    def logo_button_show(self):
        # 切换到主界面的按钮
        logo_button = QPushButton('', self)
        logo_icon = QIcon()
        logo_icon.addFile("img/fenglin_logo.png")
        logo_button.setIcon(logo_icon)
        logo_button.setIconSize(QSize(60, 60))
        logo_button.setGeometry(20, 10, 70, 90)
        logo_button.setStyleSheet("QPushButton{background:rgba(0,0,0,0);"
                                  "border:1px solid rgba(0,0,0,0);"
                                  "border-radius:5px}"
                                  )
        logo_button.setCursor(Qt.PointingHandCursor)
        logo_button.clicked.connect(self.logo_button_func)
        logo_button.show()

        logo_button_1 = QPushButton('', self)
        logo_icon_1 = QIcon()
        logo_icon_1.addFile("img/nudt_logo.png")
        logo_button_1.setIcon(logo_icon_1)
        logo_button_1.setIconSize(QSize(60, 60))
        logo_button_1.setGeometry(100, 10, 70, 90)
        logo_button_1.setStyleSheet("QPushButton{background:rgba(0,0,0,0);"
                                    "border:1px solid rgba(0,0,0,0);"
                                    "border-radius:5px}"
                                    )
        logo_button_1.setCursor(Qt.PointingHandCursor)
        logo_button_1.clicked.connect(self.logo_button_func)
        logo_button_1.show()

    def logo_button_func(self):
        self.ui.contentStackedWidget.setCurrentIndex(0)

    def tab_click_func(self):
        index = self.ui.tabWidget.currentIndex()
        # 注入时刻表
        if index == 0:
            if self.currentSWItem is not None:
                tbl_cnt_node = []
                node_id = self.currentSWItem
                # print(node_id)
                for node in self.inject_tbl:
                    if node['node_id'] == node_id:
                        tbl_cnt_node = node['entry_set']
                # print(tbl_cnt_node)
                if tbl_cnt_node:
                    rows = len(tbl_cnt_node)
                    cols = len(tbl_cnt_node[0])

                    model = QStandardItemModel(rows, cols)
                    model.setHorizontalHeaderLabels(['dbuf_id', 'time_slot'])
                    for row in range(rows):
                        col_cnt = 0
                        for col in ['dbuf_id', 'time_slot']:
                            it = QStandardItem(tbl_cnt_node[row][col])
                            model.setItem(row, col_cnt, it)
                            col_cnt += 1

                    self.ui.injectTbl.setModel(model)
                else:
                    model = QStandardItemModel()
                    self.ui.injectTbl.setModel(model)
        # 映射表
        elif index == 1:
            if self.currentSWItem is not None:
                tbl_cnt_node = []
                node_id = self.currentSWItem
                for node in self.map_tbl:
                    if node['node_id'] == node_id:
                        tbl_cnt_node = node['entry_set']
                # print(tbl_cnt_node)
                if tbl_cnt_node:
                    rows = len(tbl_cnt_node)
                    cols = len(tbl_cnt_node[0])

                    model = QStandardItemModel(rows, cols)
                    model.setHorizontalHeaderLabels(['flow_id', 'five_tuple_mask',
                                                     'ip_protocol', 'dst_port', 'src_port',
                                                     'dst_ip', 'src_ip'])
                    # model.setVerticalHeaderLabels()
                    for row in range(rows):
                        col_cnt = 0
                        for col in ['flow_id', 'five_tuple_mask',
                                    'ip_protocol', 'dst_port', 'src_port',
                                    'dst_ip', 'src_ip']:
                            it = QStandardItem(tbl_cnt_node[row][col])
                            model.setItem(row, col_cnt, it)
                            col_cnt += 1

                    self.ui.mapTbl.setModel(model)
                else:
                    model = QStandardItemModel()
                    self.ui.mapTbl.setModel(model)
        # 转发表
        elif index == 2:
            if self.currentSWItem is not None:
                tbl_cnt_node = []
                node_id = self.currentSWItem
                for node in self.fwd_tbl:
                    if node['node_id'] == node_id:
                        tbl_cnt_node = node['entry_set']
                # print(tbl_cnt_node)
                if tbl_cnt_node:
                    rows = len(tbl_cnt_node)
                    cols = len(tbl_cnt_node[0])

                    model = QStandardItemModel(rows, cols)
                    model.setHorizontalHeaderLabels(['flow_id', 'outport',
                                                     'priority', 'flow_period',
                                                     'inject_time_slot'])
                    for row in range(rows):
                        col_cnt = 0
                        for col in ['flow_id', 'outport',
                                    'priority', 'flow_period',
                                    'inject_time_slot']:
                            it = QStandardItem(tbl_cnt_node[row][col])
                            model.setItem(row, col_cnt, it)
                            col_cnt += 1

                    self.ui.forwardTbl.setModel(model)
                else:
                    model = QStandardItemModel()
                    self.ui.forwardTbl.setModel(model)
        # GCL门控表
        elif index == 3:
            if self.currentSWItem is not None:
                tbl_cnt_node = []
                node_id = self.currentSWItem
                for node in self.gcl_tbl:
                    if node['node_id'] == node_id:
                        tbl_cnt_node = node['entry_set']
                # print(tbl_cnt_node)
                if tbl_cnt_node:
                    rows = len(tbl_cnt_node)
                    cols = len(tbl_cnt_node[0])

                    model = QStandardItemModel(rows, cols)
                    model.setHorizontalHeaderLabels(['port_id', 'slot', 'gate', ])
                    # model.setVerticalHeaderLabels()
                    for row in range(rows):
                        col_cnt = 0
                        for col in ['port_id', 'slot', 'gate', ]:
                            it = QStandardItem(tbl_cnt_node[row][col])
                            model.setItem(row, col_cnt, it)
                            col_cnt += 1

                    self.ui.gclTbl.setModel(model)
                else:
                    model = QStandardItemModel()
                    self.ui.gclTbl.setModel(model)
        # 参数表
        elif index == 4:
            if self.currentSWItem is not None:
                tbl_cnt_node = {}
                node_id = self.currentSWItem
                for node in self.para_tbl:
                    if node['node_id'] == node_id:
                        tbl_cnt_node = node
                if tbl_cnt_node:
                    rows = 1  # 行
                    cols = 5  # 列

                    model = QStandardItemModel(rows, cols)
                    model.setHorizontalHeaderLabels(['qbv_or_qch',
                                                     'committed_information_rate',
                                                     'schedule_period',
                                                     'cyclelen',
                                                     'time_slot_len'])
                    row_cnt = 0
                    for row in ['qbv_or_qch',
                                'committed_information_rate',
                                'schedule_period',
                                'cyclelen',
                                'time_slot_len']:
                        if row in tbl_cnt_node.keys():
                            it = QStandardItem(tbl_cnt_node[row])
                        else:
                            it = QStandardItem()
                        model.setItem( 0, row_cnt, it)
                        row_cnt += 1

                    self.ui.paraTbl.setModel(model)
                else:
                    model = QStandardItemModel()
                    self.ui.paraTbl.setModel(model)
        # 帧消除表
        elif index == 5:
            if self.currentSWItem is not None:
                tbl_cnt_node = []
                node_id = self.currentSWItem
                for node in self.cb_tbl:
                    if node['node_id'] == node_id:
                        tbl_cnt_node = node['entry_set']
                if tbl_cnt_node:
                    rows = len(tbl_cnt_node)
                    cols = len(tbl_cnt_node[0])
                    model = QStandardItemModel(rows, cols)
                    model.setHorizontalHeaderLabels(['flow_id', 'ci'])
                    for row in range(rows):
                        col_cnt = 0
                        for col in ['flow_id', 'ci']:
                            it = QStandardItem(tbl_cnt_node[row][col])
                            model.setItem(row, col_cnt, it)
                            col_cnt += 1

                    self.ui.cbTbl.setModel(model)
                else:
                    model = QStandardItemModel()
                    self.ui.cbTbl.setModel(model)

    def select_func(self):
        self.ui.contentStackedWidget.setCurrentIndex(1)

    def link_func(self):
        self.ui.contentStackedWidget.setCurrentIndex(5)
        if self.linkFlag:
            reg_addr_xml = '../conf/addr_define.xml'
            file_path = '../input/'
            topo_xml = file_path + 'tsnbuilder/topo_feature.xml'
            output_tbl_path = '../output/tbl/'
            fwd_tbl_xml = output_tbl_path + 'fwd_tbl.xml'
            para_tbl_xml = output_tbl_path + 'para_tbl.xml'
            gcl_tbl_xml = output_tbl_path + 'gcl_tbl.xml'
            map_tbl_xml = output_tbl_path + 'map_tbl.xml'
            inject_tbl_xml = output_tbl_path + 'inject_tbl.xml'
            mac_tbl_xml = output_tbl_path + 'mac_tbl.xml'
            cb_tbl_xml = output_tbl_path + 'cb_tbl.xml'
            test_aux_tbl_xml = output_tbl_path + 'test_aux_tbl.xml'
            forward_mode_tbl_xml = output_tbl_path + 'forward_mode_tbl.xml'
            inform_tbl_xml = '../output/fl_api/inform_tbl.xml'
            conf_xml = '../output/tsnlight_plan_cfg.xml'

            link(reg_addr_xml,
                 topo_xml,
                 fwd_tbl_xml,
                 para_tbl_xml,
                 gcl_tbl_xml,
                 map_tbl_xml,
                 inform_tbl_xml,
                 inject_tbl_xml,
                 mac_tbl_xml,
                 cb_tbl_xml,
                 test_aux_tbl_xml,
                 forward_mode_tbl_xml,
                 conf_xml)

            # 将conf_xml转成conf_json
            xml_file_to_json_file(conf_xml)
            # 删除conf_xml
            os.remove(conf_xml)

            with open('../output/tsnlight_plan_cfg.json', 'r', encoding='UTF-8') as f1:
                jsonfile = f1.read()
                json_str = json.loads(jsonfile)
                json_str = json.dumps(json_str, indent=1, sort_keys=False)
                self.ui.linkTextBrowser.setText(json_str)
                self.ui.linkTextBrowser.show()
                self.ui.linkTextBrowser.setReadOnly(True)

            # 当前结果是最新结果，无需再次计算
            self.linkFlag = False
            self.ui.saveButton.setEnabled(True)

            # # 删除指定文件夹中所有的xml文件
            # remove_path = '../input/tsnbuilder/'
            # for file_name in listdir(remove_path):
            #     if file_name.endswith('.xml'):
            #         os.remove(remove_path + file_name)
            #
            # remove_path = '../output/tbl/'
            # for file_name in listdir(remove_path):
            #     if file_name.endswith('.xml'):
            #         os.remove(remove_path + file_name)
            #
            # remove_path = '../output/fl_api/'
            # for file_name in listdir(remove_path):
            #     if file_name.endswith('.xml'):
            #         os.remove(remove_path + file_name)
    def frame_text_show(self):
        self.ui.flowFileTextBrowser.setText("../input/tsnbuilder/flow_feature.json")
        self.ui.flowFileTextBrowser.setReadOnly(True)
        self.ui.topoFileTextBrowser.setText("../input/tsnbuilder/topo_feature.json")
        self.ui.topoFileTextBrowser.setReadOnly(True)
        self.ui.frameFileTextBrowser.setText("../input/algorithm/frame/conf_frame.json")
        self.ui.frameFileTextBrowser.setReadOnly(True)
        self.flowFeatureFlag = True
        self.ui.flowFeatureButton.setEnabled(True)

    def window_text_show(self):
        self.ui.flowFileTextBrowser.setText("../input/algorithm/window/flow_feature.json")
        self.ui.flowFileTextBrowser.setReadOnly(True)
        self.ui.topoFileTextBrowser.setText("../input/algorithm/window/topo_feature.json")
        self.ui.topoFileTextBrowser.setReadOnly(True)
        self.ui.frameFileTextBrowser.setText("../input/algorithm/window/conf_window.json")
        self.ui.frameFileTextBrowser.setReadOnly(True)
        self.flowFeatureFlag = False
        self.ui.flowFeatureButton.setEnabled(False)
        self.ui.assembleButton.setEnabled(False)
        self.ui.linkerButton.setEnabled(False)
        self.ui.saveButton.setEnabled(False)

    def xz_text_show(self):
        self.ui.flowFileTextBrowser.setText("../input/algorithm/xz_plan/flow_feature.json")
        self.ui.flowFileTextBrowser.setReadOnly(True)
        self.ui.topoFileTextBrowser.setText("../input/algorithm/xz_plan/topo_feature.json")
        self.ui.topoFileTextBrowser.setReadOnly(True)
        self.ui.frameFileTextBrowser.setText("../input/algorithm/xz_plan/conf_xz_plan.json")
        self.ui.frameFileTextBrowser.setReadOnly(True)
        self.flowFeatureFlag = False
        self.ui.flowFeatureButton.setEnabled(False)
        self.ui.assembleButton.setEnabled(False)
        self.ui.linkerButton.setEnabled(False)
        self.ui.saveButton.setEnabled(False)

    def itp_text_show(self):
        self.ui.flowFileTextBrowser.setText("../input/algorithm/itp/flow_feature.json")
        self.ui.flowFileTextBrowser.setReadOnly(True)
        self.ui.topoFileTextBrowser.setText("../input/algorithm/itp/topo_feature.json")
        self.ui.topoFileTextBrowser.setReadOnly(True)
        self.ui.frameFileTextBrowser.setText("../input/algorithm/itp/conf_itp.json")
        self.ui.frameFileTextBrowser.setReadOnly(True)
        self.flowFeatureFlag = False
        self.ui.flowFeatureButton.setEnabled(False)
        self.ui.assembleButton.setEnabled(False)
        self.ui.linkerButton.setEnabled(False)
        self.ui.saveButton.setEnabled(False)

    def plan(self):
        # 规划
        conf = self.ui.frameFileTextBrowser.toPlainText()
        open_planner(conf)

        self.ui.planButton.setEnabled(True)

    def frame_file_show(self):
        frame_json_file = self.ui.frameFileTextBrowser.toPlainText()
        with open(frame_json_file, 'r', encoding='UTF-8') as f1:
            jsonfile = f1.read()
            json_str = json.loads(jsonfile)
            json_str = json.dumps(json_str, indent=1, sort_keys=False)
            colorful_json = highlight(json_str, lexers.JsonLexer(), formatters.TerminalFormatter())
            # print(colorful_json)
            self.ui.FileTextBrowser.setText(json_str)
            self.ui.FileTextBrowser.show()
            self.ui.FileTextBrowser.setReadOnly(True)
        return

    def frame_file_modify(self):
        self.ui.FileTextBrowser.setReadOnly(False)
        frame_json_file = self.ui.frameFileTextBrowser.toPlainText()
        with open(frame_json_file, 'r', encoding='UTF-8') as f1:
            jsonfile = f1.read()
            json_str = json.loads(jsonfile)
            json_str = json.dumps(json_str, indent=1, sort_keys=False)
            colorful_json = highlight(json_str, lexers.JsonLexer(), formatters.TerminalFormatter())
            # print(colorful_json)
            self.ui.FileTextBrowser.setText(json_str)
            self.ui.FileTextBrowser.show()
        return

    def frame_file_save(self):
        frame_json_file = self.ui.frameFileTextBrowser.toPlainText()
        with open(frame_json_file, 'w', encoding='UTF-8') as f1:
            file_text = self.ui.FileTextBrowser.toPlainText()
            f1.write(str(file_text))
        QtWidgets.QMessageBox.information(self.ui.frameSaveButton, "information", "save success ")
        return

    def flow_file_show(self):
        flow_json_file = self.ui.flowFileTextBrowser.toPlainText()
        with open(flow_json_file, 'r', encoding='UTF-8') as f1:
            jsonfile = f1.read()
            json_str = json.loads(jsonfile)
            json_str = json.dumps(json_str, indent=1, sort_keys=False)
            self.ui.FileTextBrowser.setText(json_str)
            self.ui.FileTextBrowser.show()
            self.ui.FileTextBrowser.setReadOnly(True)
        return

    def flow_file_modify(self):
        self.ui.FileTextBrowser.setReadOnly(False)
        flow_json_file = self.ui.flowFileTextBrowser.toPlainText()
        with open(flow_json_file, 'r', encoding='UTF-8') as f1:
            jsonfile = f1.read()
            json_str = json.loads(jsonfile)
            json_str = json.dumps(json_str, indent=1, sort_keys=False)
            self.ui.FileTextBrowser.setText(json_str)
            self.ui.FileTextBrowser.show()
        return

    def flow_file_save(self):
        flow_json_file = self.ui.flowFileTextBrowser.toPlainText()
        with open(flow_json_file, 'w', encoding='UTF-8') as f1:
            file_text = self.ui.FileTextBrowser.toPlainText()
            f1.write(str(file_text))
        QtWidgets.QMessageBox.information(self.ui.flowSaveButton, "information", "save success ")
        return

    def topo_file_show(self):
        topo_json_file = self.ui.topoFileTextBrowser.toPlainText()
        with open(topo_json_file, 'r', encoding='UTF-8') as f1:
            jsonfile = f1.read()
            json_str = json.loads(jsonfile)
            json_str = json.dumps(json_str, indent=1, sort_keys=False)
            self.ui.FileTextBrowser.setText(json_str)
            self.ui.FileTextBrowser.show()
            self.ui.FileTextBrowser.setReadOnly(True)
        return

    def topo_file_modify(self):
        self.ui.FileTextBrowser.setReadOnly(False)
        topo_json_file = self.ui.topoFileTextBrowser.toPlainText()
        with open(topo_json_file, 'r', encoding='UTF-8') as f1:
            jsonfile = f1.read()
            json_str = json.loads(jsonfile)
            json_str = json.dumps(json_str, indent=1, sort_keys=False)
            self.ui.FileTextBrowser.setText(json_str)
            self.ui.FileTextBrowser.show()
        return

    def topo_file_save(self):
        topo_json_file = self.ui.topoFileTextBrowser.toPlainText()
        with open(topo_json_file, 'w', encoding='UTF-8') as f1:
            file_text = self.ui.FileTextBrowser.toPlainText()
            f1.write(str(file_text))
        QtWidgets.QMessageBox.information(self.ui.flowSaveButton, "information", "save success ")
        return

    # 目录记忆功能
    def cdLastDir(self, hint_str):
        m, _ = QtWidgets.QFileDialog.getOpenFileName(self,
                                                     "%s" % hint_str,
                                                     self.last_path,
                                                     "JSON文档 (*.json);;XML文档 (*.xml)")
        try:
            # 保存当前目录的路径到配置文件中，另外如果不存在'tmp/.temp'文件该函数会自动创建
            self.setting.setValue('LastFilePath', os.path.dirname(m))
            # 将路径设为''程序会使用上一次的路径
            self.last_path = ''
        except Exception as e:
            # 发生异常，弹窗警告
            msg = QtWidgets.QMessageBox.warning(self, "Warning", "文件打开失败%s" % e,
                                                # buttons=QtWidgets.QMessageBox.Ok,
                                                defaultButton=QtWidgets.QMessageBox.Ok)
        return m

    # 选择文件按钮的槽函数
    def chooseFrameFile_func(self):
        m = self.cdLastDir("选取帧配置文件")
        self.ui.frameFileTextBrowser.setText(m)

    # 选择文件按钮的槽函数
    def chooseStreamFile_func(self):
        m = self.cdLastDir("选取流文件")
        self.ui.flowFileTextBrowser.setText(m)

    # 选择文件按钮的槽函数
    def chooseTopoFile_func(self):
        m = self.cdLastDir("选取拓扑文件")
        self.ui.topoFileTextBrowser.setText(m)

    # 确认按钮的槽函数
    def completeButtonCopyFile_func(self):
        # 容错逻辑，判断文件是否是对应内容的文件
        frame_json_file = self.ui.frameFileTextBrowser.toPlainText()
        if frame_json_file == '':
            QtWidgets.QMessageBox.critical(self.ui.completeButton, "error", "please select frame_json_file")
            print('please select frame_json_file')
            return

        stream_json_file = self.ui.flowFileTextBrowser.toPlainText()
        if stream_json_file == '':
            QtWidgets.QMessageBox.critical(self.ui.completeButton, "error", "please select stream_json_file")
            print('please select stream_json_file')
            return

        topo_json_file = self.ui.topoFileTextBrowser.toPlainText()
        if topo_json_file == '':
            QtWidgets.QMessageBox.critical(self.ui.completeButton, "error", "please select topo_json_file")
            print('please select topo_json_file')
            return

    # 流特征按钮的回调函数
    def flow_feature_func(self):
        self.ui.contentStackedWidget.setCurrentIndex(2)
        if self.planWidget is None:
            self.planWidget = topoPaintWidget(self.ui.flowFeaturePage,
                                              self)
        # 如果该页面不空，并且需要重新规划
        elif self.planWidget is not None and self.flowFeatureFlag is True:
            # 将页面上所有按钮都清空
            for button in self.tsn_button_set:
                button.setParent(None)
                button.label.setParent(None)
                button.sched_label.setParent(None)

            self.tsn_button_set = []

        if self.flowLabelWidget is None:
            # 规划按钮
            # 设置规划页面介绍流路径的标签
            self.flowLabelWidget = QLabel(self.ui.flowFeaturePage)
            self.flowLabelWidget.setGeometry(150, 50, 880, 80)
            self.flowLabelWidget.setAlignment(QtCore.Qt.AlignCenter)
            self.flowLabelWidget.setStyleSheet("QWidget{border:1px solid white;"
                                               "border-radius:5px; "
                                               "background-color: rgb(0, 93, 152);"
                                               "color: rgb(255, 255, 255);"
                                               "font-size:20px;}")
            self.flowLabelWidget.show()

        if self.streamListWidget is None:
            self.streamListWidget = streamListWidget(self.ui.flowFeaturePage,
                                                     self,
                                                     self.planWidget)
        # 需要重新规划
        elif self.streamListWidget is not None and self.flowFeatureFlag is True:
            print(self.streamListWidget.stream_item_list)
            for item in self.streamListWidget.stream_item_list:
                self.streamListWidget.takeItem(self.streamListWidget.row(item))
            self.streamListWidget.stream_item_list = []

        if self.flowFeatureFlag:
            # 预处理
            file_path = '../input/'
            processing(file_path)

            flow_file = file_path + 'tsnbuilder/flow_feature.xml'
            topo_file = file_path + 'tsnbuilder/topo_feature.xml'
            plan_file = file_path + 'tsnbuilder/plan.xml'

            # 填写调度周期和时间槽长度
            slot, sched_cycle = get_slot_and_hyper_period(flow_file,
                                                          topo_file)
            # self.flowFeaturePage_label_text = '调度周期: %d us, 时间槽长度:%d us, 时间槽个数：%s 个' \
            #                            % (sched_cycle, slot, int(sched_cycle / slot))
            self.flowFeaturePage_label_text = '调度周期: %d us' % sched_cycle
            self.flowLabelWidget.setText(self.flowFeaturePage_label_text)

            # 初始化tsn节点
            para_file = file_path + '/tsnbuilder/cfg_parameter.xml'
            node_set = init_topo(topo_file, para_file)
            # 初始化tsn_button
            for node in node_set:
                title = ''
                tsn_button = TSNButton(title,
                                       self.planWidget,
                                       node['node_id'],
                                       node['node_type'])
                tsn_button.show()
                self.tsn_button_set.append(tsn_button)

            # 初始化tsn_button的所有子button
            link_node_port_set = get_link_node_port_set(topo_file)
            for item in link_node_port_set:
                src_node_id = item['src_node_id']
                dst_node_id = item['dst_node_id']
                parent_button = None
                child_button = None
                for button in self.tsn_button_set:
                    if button.node_id == src_node_id:
                        parent_button = button
                    if button.node_id == dst_node_id:
                        child_button = button
                if parent_button is not None and child_button is not None:
                    parent_button.appendChildren(child_button)

            # 从flow_xml中获取所有的流量
            stream_set, path_set = get_stream(flow_file, topo_file)
            plan_set = get_plan(plan_file, topo_file)

            self.streamListWidget.set_stream_path_plan_list(stream_set,
                                                            path_set,
                                                            plan_set)

            # 当前结果就是最新结果，不需要再次进行计算
            self.flowFeatureFlag = False
            # 使能编译按钮
            frame_json_file = self.ui.frameFileTextBrowser.toPlainText()
            file_name = frame_json_file.split("/")[-1]
            if file_name == 'conf_frame.json':
                self.assembleFlag = True
                self.ui.assembleButton.setEnabled(True)
            # else:
            #     self.ui.assembleButton.setEnabled(False)
            #     self.ui.linkerButton.setEnabled(False)
            #     self.ui.saveButton.setEnabled(False)

    def plan_func(self):
        self.ui.contentStackedWidget.setCurrentIndex(3)
        conf_file = self.ui.frameFileTextBrowser.toPlainText()
        solution_file = os.path.dirname(conf_file) + '/solution_gcl.json'
        with open(solution_file, 'r', encoding='UTF-8') as f1:
            jsonfile = f1.read()
            # json_str = json.loads(jsonfile)
            # json_str = json.dumps(json_str, indent=1, sort_keys=False)
            self.ui.planTextBrowser.setText(jsonfile)
            self.ui.planTextBrowser.show()
            self.ui.planTextBrowser.setReadOnly(True)

    # 编译按钮的回调函数
    def assemble_func(self):
        self.ui.contentStackedWidget.setCurrentIndex(4)

        if self.topoListWidget is None:
            # 添加topoListWidget
            self.topoListWidget = topoListWidget(self.ui.assemblePage,
                                                 self,
                                                 self.ui.tabWidget,
                                                 )
        # 说明要重新编译
        elif self.topoListWidget is not None and self.assembleFlag is True:
            for item in self.topoListWidget.sw_item_list:
                self.topoListWidget.takeItem(self.topoListWidget.row(item))
            self.topoListWidget.sw_item_list = []

        if self.assembleFlag:
            # 输入文件
            file_path = '../input/'
            flow_xml = file_path + 'tsnbuilder/flow_feature.xml'
            topo_xml = file_path + 'tsnbuilder/topo_feature.xml'
            solution_xml = file_path + 'tsnbuilder/plan.xml'
            para_xml = file_path + 'tsnbuilder/cfg_parameter.xml'
            mac_xml = file_path + 'tsnbuilder/static_mac_cfg.xml'
            test_aux_xml = file_path + 'tsnbuilder/test_aux.xml'
            forward_mode_xml = file_path + 'tsnbuilder/forward_mode.xml'

            # 输出文件
            fl_api_xml_folder = '../output/fl_api/'
            output_tbl_path = '../output/tbl/'
            fwd_tbl_xml = output_tbl_path + 'fwd_tbl.xml'
            gcl_tbl_xml = output_tbl_path + 'gcl_tbl.xml'
            inform_tbl_xml = fl_api_xml_folder + 'inform_tbl.xml'
            inject_tbl_xml = output_tbl_path + 'inject_tbl.xml'
            map_tbl_xml = output_tbl_path + 'map_tbl.xml'
            para_tbl_xml = output_tbl_path + 'para_tbl.xml'
            mac_tbl_xml = output_tbl_path + 'mac_tbl.xml'
            cb_tbl_xml = output_tbl_path + 'cb_tbl.xml'
            test_aux_tbl_xml = output_tbl_path + 'test_aux_tbl.xml'
            forward_mode_tbl_xml = output_tbl_path + 'forward_mode_tbl.xml'

            filelist = os.listdir(output_tbl_path)
            for f in filelist:
                os.remove(output_tbl_path + f)

            assemble(flow_xml,
                     topo_xml,
                     solution_xml,
                     para_xml,
                     mac_xml,
                     test_aux_xml,
                     forward_mode_xml,
                     fl_api_xml_folder,
                     fwd_tbl_xml,
                     gcl_tbl_xml,
                     inform_tbl_xml,
                     inject_tbl_xml,
                     map_tbl_xml,
                     para_tbl_xml,
                     mac_tbl_xml,
                     cb_tbl_xml,
                     test_aux_tbl_xml,
                     forward_mode_tbl_xml
                     )

            self.inject_tbl = get_inject_tbl(inject_tbl_xml)
            self.map_tbl = get_map_tbl(map_tbl_xml)
            self.fwd_tbl = get_tbl(fwd_tbl_xml)
            self.gcl_tbl = get_gcl_tbl(gcl_tbl_xml)
            self.para_tbl = get_para_tbl(para_tbl_xml)
            self.cb_tbl = get_cb_tbl(cb_tbl_xml)
            # self.inform_tbl = get_tbl(inform_tbl_xml)
            # self.aux_tbl = get_aux_tbl(test_aux_tbl_xml)

            topo_list = init_topo(topo_xml, para_xml)
            sw_list = []
            for node in topo_list:
                if node['node_type'] == 'SW' or node['node_type'] == 'NIC':
                    sw_list.append(node)

            self.topoListWidget.set_sw_item(sw_list)

            # 当前结果就是最新结果，不需要再次进行计算
            self.assembleFlag = False
            # 使能链接按钮
            self.linkFlag = True
            self.ui.linkerButton.setEnabled(True)

            # 将输出文件由xml转为json
            output_path = '../output/tbl/'
            for file_name in listdir(output_path):
                if file_name.endswith('.xml'):
                    xml_file = output_path + file_name
                    xml_file_to_json_file(xml_file)

            output_path = '../output/fl_api/'
            for file_name in listdir(output_path):
                if file_name.endswith('.xml'):
                    xml_file = output_path + file_name
                    xml_file_to_json_file(xml_file)

    def save_file(self):
        save_path = QFileDialog.getExistingDirectory(self, "选择保存文件夹")
        shutil.copy('../output/tsnlight_plan_cfg.json', save_path)
        QtWidgets.QMessageBox.information(self.ui.saveButton, "information", "save conf file success ")


if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()

    # apply_stylesheet(app, theme='light_amber.xml')
    # https://github.com/UN-GCPDS/qt-material/blob/master/README.md#usage
    app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api='pyside6'))

    window.show()
    sys.exit(app.exec())
